package com.lcx.core;

import com.lcx.annotation.HandCoreInit;
import com.lcx.annotation.JsObject;
import com.lcx.bean.StageClosingLisenerBean;
import com.lcx.core.exector.Exector;
import com.lcx.core.exector.JsExector;
import com.lcx.core.exector.SyncExector;
import com.lcx.listener.WebReloadListener;
import com.lcx.util.CopyFileUtil;
import com.lcx.util.JsTemplateUtil;
import com.lcx.util.ReflectUtil;
import com.lcx.util.StringUtil;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.web.WebEngine;
import javafx.stage.Stage;
import netscape.javascript.JSObject;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 核心处理类
 */
public class HandleCore {

    private WebEngine webEngine;

    private boolean state;

    private boolean startState=true;

    private final SyncExector syncExector = new SyncExector();

    private Exector exector;

    private Stage stage;

    private static String jsInitCode;

    private StageClosingLisenerBean closingLisenerBean;

    private Class<?> targetClass;

    public HandleCore(WebEngine webEngine){
        newInstance(webEngine,null);
    }

    public HandleCore(WebEngine webEngine,Class<?> clazz){
        newInstance(webEngine,clazz);
    }

    private void newInstance(WebEngine webEngine,Class<?> clazz){
        this.targetClass = clazz==null?getClass():clazz;
        if(jsInitCode ==  null){
            synchronized (HandleCore.class){
                if(jsInitCode == null){
                    jsInitCode = CopyFileUtil.readTextFromStream(getClass().getResourceAsStream("/inner.js"),"UTF-8");
                }
            }
        }
        Scanner.createContext();
        this.webEngine = webEngine;
        exector  = new Exector(webEngine,syncExector);
        JsExector jsExector = Scanner.getBean(JsExector.class);
        Field field = ReflectUtil.getFieldByName("webEngine",JsExector.class);
        field.setAccessible(true);
        try {
            field.set(jsExector,webEngine);
        } catch (Exception e) {
            e.printStackTrace();
        }
        field.setAccessible(false);
    }

    /**
     * 会覆盖stage原有的oncloserequest。
     * @param stage
     */
    public void setStage(Stage stage){
        this.stage = stage;
        closingLisenerBean = new StageClosingLisenerBean();
        stage.setOnCloseRequest(e->{
//            e.consume(); 暂时不让应用阻止关闭，关闭前做一些事情。
            List<StageClosingLisenerBean.OnCloseRequest> closing = closingLisenerBean.getAll();
            if(closing != null){
                closing.stream().forEach(c->{
                    try{
                        c.closing();
                    }catch(Exception t){}
                });
            }

            try{
                exit();
            }catch (Exception e2){}
        });
    }

    public void init(){
        Set<Map.Entry<String,Object>> beans = Scanner.getAllBeans();
        if(beans != null && !beans.isEmpty()){
            beans.stream().forEach(e->{
                JsObject jsObject;
                if((jsObject=e.getValue().getClass().getAnnotation(JsObject.class))!=null){
                    String name = jsObject.name();
                    if(!StringUtil.isNotNull(name)){
                        name = StringUtil.modifySCName(e.getValue().getClass().getSimpleName());
                    }
                    try {
                        HandleCore.addJsObject(name,e.getValue());
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }

                    List<Method> methods = ReflectUtil.getMethods(e.getValue().getClass());
                    if(methods!=null && !methods.isEmpty()){
                        methods.stream().forEach(c->{
                            HandCoreInit i = c.getAnnotation(HandCoreInit.class);
                            if(i!=null){
                                try {
                                    if(c.getParameterCount() == 0){
                                        c.invoke(e.getValue(),null);
                                    }else{
                                        c.invoke(e.getValue(),this);
                                    }
                                } catch (Exception e1) {
                                    throw new RuntimeException(e1);
                                }
                            }
                        });
                    }
                }
            });
        }

        webEngine.setJavaScriptEnabled(true);
        if(webEngine.getOnAlert()==null)
        webEngine.setOnAlert(e->{
            Dialog d = new Dialog();
            d.getDialogPane().setContentText(e.getData());
            d.setTitle("来自网页的消息");
            d.getDialogPane().getButtonTypes().add(ButtonType.OK);
            d.showAndWait();
        });
        if(webEngine.getConfirmHandler()==null)
        webEngine.setConfirmHandler(e->{
            Dialog<ButtonType> confirm = new Dialog<>();
            confirm.getDialogPane().setContentText(e);
            confirm.getDialogPane().getButtonTypes().addAll(ButtonType.YES, ButtonType.NO);
            boolean result = confirm.showAndWait().filter(ButtonType.YES::equals).isPresent();
            return result ;
        });
        webEngine.getLoadWorker().progressProperty().addListener((a,b,c)->{
            if(startState && !state){
                exector.stopAllMission();
                startState=false;
            }
            if(webEngine.getDocument()!=null){
                if(!state){
                    readyToInit();
                    state = true;
                }
            }else{
                state=false;
            }
            if(c.doubleValue()==1 && !state){
                readyToInit();
                state = true;
            }
        });
    }

    private void readyToInit(){
        startState=true;
        URL url = targetClass.getProtectionDomain().getCodeSource().getLocation();
        String jar="";
        String temp = url.getPath();
        if(temp.contains(":")){
            temp = temp.substring(1);
            if(new File(temp).isFile()){
                jar="jar:";
            }
        }
        String path = jar+url.toExternalForm();
        if(path.endsWith("/")) path=path.substring(0,path.length()-1);
        webEngine.executeScript("var contextName='"+path+"';");
        webEngine.executeScript(jsInitCode);
        webEngine.executeScript(JsTemplateUtil.generateSyncExecProxyMethod());
        JSObject jsObject = (JSObject) webEngine.executeScript("window");
        jsObjects.entrySet().stream().forEach(e->{
            JsTemplate jsTemplate = new JsTemplate(e.getValue(),webEngine.getLocation());
            String js = jsTemplate.collect();
            webEngine.executeScript(js);
        });
        jsObject.setMember(JsTemplateUtil.syncExector,syncExector);
        jsObject.setMember(JsTemplateUtil.exector,exector);
        if(stage!=null){
            Object obj = webEngine.executeScript("document.title");
            if(obj != null) stage.setTitle(obj.toString());
        }

    }

    public static void addJsObject(String name,Object obj){
        if(StringUtil.isNotNull(name)){
            jsObjects.put(name,obj);
        }
        List<Method> methods = ReflectUtil.getMethods(obj.getClass());
        if(methods!=null){
            for(Method m:methods){
                if(m.getAnnotation(JsObject.class)!=null){
                    Map<String,Method> map = invokeMethods.getOrDefault(name,new HashMap<>());
                    map.put(m.getName(),m);
                    invokeMethods.put(name,map);
                }
            }
        }
        Class<?> clazzs[] = obj.getClass().getInterfaces();
        if(clazzs!= null && clazzs.length>0){
            for(Class<?> c:clazzs){
                if(c == WebReloadListener.class){
                    jsWebReloadListenerObjects.put(name,obj);
                }
            }
        }
    }

    public static void removeJsObject(String name){
        jsObjects.remove(name);
        jsWebReloadListenerObjects.remove(name);
        invokeMethods.remove(name);
    }

    public static Object getJsObject(String name){
        return jsObjects.get(name);
    }

    public static Object getWebReloadListenerObject(String name){
        return jsWebReloadListenerObjects.get(name);
    }

    public static Method getInvokeMethod(String name,String mName){
        Map<String,Method> m = invokeMethods.get(name);
        if(m!=null){
            return m.get(mName);
        }
        return null;
    }

    public StageClosingLisenerBean getOnClosingEvent(){
        return closingLisenerBean;
    }

    public void exit(){
        exector.destoryTaskMission();
    }

    private static Map<String,Object> jsObjects = new HashMap<>(2);
    private static Map<String,Object> jsWebReloadListenerObjects = new HashMap<>(2);
    private static Map<String,Map<String,Method>> invokeMethods = new HashMap<>(10);
}
